/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */

package com.aliyun.oss.model;

import java.util.ArrayList;
import java.util.List;

public class SetBucketCORSRequest extends WebServiceRequest {
	
	private static int MAX_CORS_RULE_LIMIT = 10;
	private static char ASTERISK = '*';
	private static String[] ALL_ALLOWED_METHODS = { "GET", "PUT", "DELETE", "POST", "HEAD" };

	private String bucketName;
	private List<CORSRule> corsRules=new ArrayList<CORSRule>();
	
	public SetBucketCORSRequest(String bucketName) {
		this.bucketName = bucketName;
	}
	
	public void addCorsRule(CORSRule corsRule) {
		checkCorsValidity(corsRule);
		this.corsRules.add(corsRule);
	}
	
	private void checkCorsValidity(CORSRule corsRule) {
		if (corsRule == null) {
			throw new IllegalArgumentException("corsRule should not be null or empty.");
		}
		
		if (this.corsRules.size() >= MAX_CORS_RULE_LIMIT) {
			throw new IllegalArgumentException("One bucket not allowed exceed ten items of CORS Rules.");
		}
		
		// At least one item of allowed origins
		if (corsRule.getAllowedOrigins().isEmpty()) {
			throw new IllegalArgumentException("Required field 'AllowedOrigins' should not be empty.");
		}
		
		// At least one item of allowed methods
		if (corsRule.getAllowedMethods().isEmpty()) {			
			throw new IllegalArgumentException("Required field 'AllowedMethod' should not be empty.");
		}
		
		// At most one asterisk wildcard in allowed origins
		for (String origin : corsRule.getAllowedOrigins()) {
			if (countOfAsterisk(origin) > 1) {
				throw new IllegalArgumentException("At most one '*' wildcard in allowd origin.");
			}
		}
		
		// Unsupported method
		for (String method : corsRule.getAllowedMethods()) {
			if (!isAllowedMethod(method)) {
				throw new IllegalArgumentException("Unsupported method " + method +
						", (GET,PUT,DELETE,POST,HEAD)");
			}
		}
		
		// At most one asterisk wildcard in allowed headers
		for (String header : corsRule.getAllowedHeaders()) {
			if (countOfAsterisk(header) > 1) {
				throw new IllegalArgumentException("At most one '*' wildcard in allowd header.");
			}
		}
		
		// Not allow to use any asterisk wildcard in allowed origins
		for (String header : corsRule.getExposeHeaders()) {
			if (countOfAsterisk(header) > 0) {
				throw new IllegalArgumentException("Not allow to use any '*' wildcard in expose header.");
			}
		}
	}
	
	private static int countOfAsterisk(String str) {
		if (str == null || str.equals("")) {
			return 0;
		}
		
		int from = 0;
		int pos = -1;
		int count = 0;
		int len = str.length();
		do {
			pos = str.indexOf(ASTERISK, from);
			if (pos != -1) {
				count++;
				from = pos + 1;
			} 
		} while (pos != -1 && from < len);
		
		return count;
	}
	
	private static boolean isAllowedMethod(String method) {
		if (method == null || method.equals("")) {
			return false;
		}
		
		for (String m : ALL_ALLOWED_METHODS) {
			if (m.equals(method)) {
				return true;
			}
		}
		return false;
	}

	public String getBucketName() {
		return bucketName;
	}

	public void setBucketName(String bucketName) {
		this.bucketName = bucketName;
	}
	
	public List<CORSRule> getCorsRules() {
		return corsRules;
	}

	public void setCorsRules(List<CORSRule> corsRules) {
		if (corsRules == null || corsRules.isEmpty()) {
			throw new IllegalArgumentException("corsRules should not be null or empty.");
		}
		
		if (corsRules.size() > MAX_CORS_RULE_LIMIT) {
			throw new IllegalArgumentException("One bucket not allowed exceed ten items of CORS Rules.");
		}
		
		this.corsRules.clear();
		this.corsRules.addAll(corsRules);
	}
	
	public void clearCorsRules() {
		this.corsRules.clear();
	}
	
	public static class CORSRule {
		private List<String> allowedOrigins = new ArrayList<String>();
		private List<String> allowedMethods = new ArrayList<String>();
		private List<String> allowedHeaders = new ArrayList<String>();
		private List<String> exposeHeaders = new ArrayList<String>();
		
		private Integer maxAgeSeconds;
		
		public void addAllowdOrigin(String allowedOrigin) {
			if (allowedOrigin != null && !allowedOrigin.trim().isEmpty()) {
				this.allowedOrigins.add(allowedOrigin);
			}
		}

		public List<String> getAllowedOrigins() {
			return allowedOrigins;
		}

		public void setAllowedOrigins(List<String> allowedOrigins) {
			this.allowedOrigins.clear();
			if (allowedOrigins != null && !allowedOrigins.isEmpty()) {
				this.allowedOrigins.addAll(allowedOrigins);
			}
		}
		
		public void clearAllowedOrigins() {
			this.allowedOrigins.clear();
		}

		public void addAllowedMethod(String allowedMethod) {
			if (allowedMethod != null && !allowedMethod.trim().isEmpty()) {
				this.allowedMethods.add(allowedMethod);
			}
		}
		
		public List<String> getAllowedMethods() {
			return allowedMethods;
		}

		public void setAllowedMethods(List<String> allowedMethods) {
			this.allowedMethods.clear();
			if (allowedMethods != null && !allowedMethods.isEmpty()) {
				this.allowedMethods.addAll(allowedMethods);
			}
		}
		
		public void clearAllowedMethods() {
			this.allowedMethods.clear();
		}
		
		public void addAllowedHeader(String allowedHeader) {
			if (allowedHeader != null && !allowedHeader.trim().isEmpty()) {
				this.allowedHeaders.add(allowedHeader);
			}
		}

		public List<String> getAllowedHeaders() {
			return allowedHeaders;
		}

		public void setAllowedHeaders(List<String> allowedHeaders) {
			this.allowedHeaders.clear();
			if (allowedHeaders != null && !allowedHeaders.isEmpty()) {
				this.allowedHeaders.addAll(allowedHeaders);
			}
		}
		
		public void clearAllowedHeaders() {
			this.allowedHeaders.clear();
		}

		public void addExposeHeader(String exposeHeader) {
			if (exposeHeader != null && !exposeHeader.trim().isEmpty()) {
				this.exposeHeaders.add(exposeHeader);
			}
		}
		
		public List<String> getExposeHeaders() {
			return exposeHeaders;
		}

		public void setExposeHeaders(List<String> exposeHeaders) {
			this.exposeHeaders.clear();
			if (exposeHeaders != null && !exposeHeaders.isEmpty()) {
				this.exposeHeaders.addAll(exposeHeaders);
			}
		}
		
		public void clearExposeHeaders() {
			this.exposeHeaders.clear();
		}

		public Integer getMaxAgeSeconds() {
			return maxAgeSeconds;
		}

		public void setMaxAgeSeconds(Integer maxAgeSeconds) {
			this.maxAgeSeconds = maxAgeSeconds;
		}
	}
}
